home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / terminal / sysline-.1 / sysline- / sysline-1.1 / sysline.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-16  |  33.1 KB  |  1,511 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted provided
  6.  * that: (1) source distributions retain this entire copyright notice and
  7.  * comment, and (2) distributions including binaries display the following
  8.  * acknowledgement:  ``This product includes software developed by the
  9.  * University of California, Berkeley and its contributors'' in the
  10.  * documentation or other materials provided with the distribution and in
  11.  * all advertising materials mentioning features or use of this software.
  12.  * Neither the name of the University nor the names of its contributors may
  13.  * be used to endorse or promote products derived from this software without
  14.  * specific prior written permission.
  15.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  16.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  17.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  */
  19.  
  20. #ifndef lint
  21. char copyright[] =
  22. "@(#) Copyright (c) 1980 Regents of the University of California.\n\
  23.  All rights reserved.\n";
  24. #endif /* not lint */
  25.  
  26. #ifndef lint
  27. /*static char sccsid[] = "@(#)sysline.c    5.16 (Berkeley) 6/24/90";*/
  28. #endif /* not lint */
  29.  
  30. /*
  31.  *
  32.  * Linux port:
  33.  * v 1.0    May 11th 1995    bjdouma@xs4all.nl (Bauke Jan Douma)
  34.  * v 1.1    Mar 16th 1996    bjdouma@xs4all.nl (Bauke Jan Douma)
  35.  *
  36.  */
  37.  
  38. /*
  39.  * sysline - system status display on 25th line of terminal
  40.  * j.k.foderaro
  41.  *
  42.  * Prints a variety of information on the special status line of terminals
  43.  * that have a status display capability.  Cursor motions, status commands,
  44.  * etc. are gleaned from /etc/termcap, or an appropriate terminfo file.
  45.  * By default, ALL information is printed, and flags are given on the command
  46.  * line to disable the printing of information.  The information and
  47.  * disabling flags are:
  48.  *
  49.  *  flag    what
  50.  *  -----    ----
  51.  *        time of day
  52.  *        load average and change in load average in the last 5 mins
  53.  *        number of user logged on
  54.  *   -p        # of processes the users owns which are runnable and the
  55.  *          number which are suspended.  Processes whose parent is 1
  56.  *          are not counted.
  57.  *   -l        users who've logged on and off.
  58.  *   -m        summarize new mail which has arrived
  59.  *
  60.  *  <other flags>
  61.  *   -r        use non reverse video
  62.  *   -c        turn off 25th line for 5 seconds before redisplaying.
  63.  *   -b        beep once one the half hour, twice on the hour
  64.  *   +N        refresh display every N seconds.
  65.  *   -i        print pid first thing
  66.  *   -e        do simple print designed for an emacs buffer line
  67.  *   -w        do the right things for a window
  68.  *   -h        print hostname between time and load average
  69.  *   -D        print day/date before time of day
  70.  *   -d        debug mode - print status line data in human readable format
  71.  *   -q        quiet mode - don't output diagnostic messages
  72.  *   -s        print Short (left-justified) line if escapes not allowed
  73.  *   -j        Print left Justified line regardless
  74.  */
  75.  
  76. #include <stdlib.h>
  77. #include <string.h>
  78. #include <unistd.h>
  79. #include <stdio.h>
  80. #include <stdarg.h>
  81. #include <sys/ioctl.h>
  82. #include <sys/param.h>
  83. #include <sys/types.h>
  84. #include <sys/signal.h>
  85. #include <utmp.h>
  86. #include <ctype.h>
  87. #include <termio.h>
  88. #include <sys/dir.h>
  89. #include <sys/stat.h>
  90. #include <fcntl.h>
  91. #include <sys/time.h>
  92. #include <termcap.h>
  93. #include "config.h"
  94.  
  95. #ifdef RWHO
  96.     #include "rwhod.h"
  97.     #define    DOWN_THRESHOLD    (11 * 60)
  98.     struct remotehost 
  99.     {
  100.         char *rh_host;
  101.         int rh_file;
  102.     } 
  103.      remotehost[10];
  104.     int nremotes = 0;
  105. #endif
  106.  
  107. #include "pathnames.h"
  108. double _avenrun[3];        /* used for storing load averages */
  109.  
  110. /*
  111.  * In order to determine how many people are logged on and who has
  112.  * logged in or out, we read in the /etc/utmp file. We also keep track of
  113.  * the previous utmp file.
  114.  */
  115. int ut = -1;            /* the file descriptor */
  116. struct utmp *new, *old;    
  117. char *ttystatus;        /* per tty status bits, see below */
  118. int nentries;            /* number of utmp entries */
  119.  
  120.     /* string lengths for printing */
  121. #define LINE_SIZE    UT_LINESIZE
  122. #define NAME_SIZE    UT_NAMESIZE
  123.  
  124. /*
  125.  * Status codes to say what has happened to a particular entry in utmp.
  126.  * NOCH means no change, ON means new person logged on,
  127.  * OFF means person logged off.
  128.  */
  129. #define NOCH    0
  130. #define ON    0x1
  131. #define OFF    0x2
  132.  
  133. #ifdef WHO
  134.     char whofilename[100];
  135.     char whofilename2[100];
  136. #endif
  137.  
  138. #ifdef HOSTNAME
  139.     char hostname[MAXHOSTNAMELEN+1];    /* one more for null termination */
  140. #endif
  141.  
  142. char lockfilename[100];        /* if exists, will prevent us from running */
  143.  
  144.     /* flags which determine which info is printed */
  145. int mailcheck = 1;    /* m - do biff like checking of mail */
  146. int proccheck = 1;    /* p - give information on processes */
  147. int logcheck = 1;     /* l - tell who logs in and out */
  148. int hostprint = 0;    /* h - print out hostname */
  149. int dateprint = 0;    /* h - print out day/date */
  150. int quiet = 0;        /* q - hush diagnostic messages */
  151.  
  152.     /* flags which determine how things are printed */
  153. int clr_bet_ref = 0;    /* c - clear line between refeshes */
  154. int reverse = 1;    /* r - use reverse video */
  155. int shortline = 0;    /* s - short (left-justified) if escapes not allowed */
  156. int leftline = 0;    /* j - left-justified even if escapes allowed */
  157.  
  158.     /* flags which have terminal do random things    */
  159. int do_beep = 0;    /* b - beep every half hour and twice every hour */
  160. int printid = 0;    /* i - print pid of this process at startup */
  161. int synch = 1;        /* synchronize with clock */
  162.  
  163.     /* select output device (status display or straight output) */
  164. int emacs = 0;        /* e - assume status display */
  165. int window = 0;        /* w - window mode */
  166. int dbug = 0;        /* d - debug */
  167.  
  168. int revtime = 1;
  169.  
  170.     /* used by mail checker */
  171. off_t mailsize = 0;
  172. off_t linebeg = 0;        /* place where we last left off reading */
  173.  
  174.     /* things used by the string routines */
  175. int chars;            /* number of printable characters */
  176. char *sp;
  177. char strarr[512];        /* big enough now? */
  178.     /* flags to stringdump() */
  179. char sawmail;            /* remember mail was seen to print bells */
  180. char mustclear;            /* status line messed up */
  181.  
  182.     /* strings which control status line display */
  183. #ifdef TERMINFO
  184.     char    *bel;
  185.     char    *rev_out;
  186.     char    *rev_end;
  187.     char    *tparm();
  188. #else
  189.     /* as long as Linux has gnu or gnu-based termcap, let it allocate storage */
  190.     #define _HAVE_GNU_TERMCAP_
  191.     #if defined( _HAVE_GNU_TERMCAP_ )
  192.         char    *bel;
  193.         char    *rev_out;
  194.         char    *rev_end;
  195.         char    *clr_eol;
  196.     #else
  197.         char    bel[32];
  198.         char    rev_out[32];
  199.         char    rev_end[32];
  200.         char    clr_eol[64];
  201. #endif
  202.     int    eslok;        /* escapes on status line okay (reverse, cursor addressing) */
  203.     #define tparm(cap,parm)    tgoto((cap),0,(parm))
  204.     char    *tgoto();
  205. #endif
  206.  
  207. char    *arrows;
  208. char    tsl[64];
  209. char    fsl[64];
  210. char    dsl[64];
  211. int    cols;
  212. int    hasws = 0;        /* is "ws" explicitly defined? */
  213. char    nm_colors[32]="";
  214. char    rv_colors[32]="";
  215.  
  216.     /* to deal with window size changes */
  217. #ifdef SIGWINCH
  218.     void sigwinch();
  219.     char winchanged;    /* window size has changed since last update */
  220. #endif
  221.  
  222.     /* random globals */
  223. char *username;
  224. char *ourtty;            /* keep track of what tty we're on */
  225. char *progname;
  226. struct stat stbuf, mstbuf;    /* mstbuf for mail check only */
  227. unsigned delay = DEFDELAY;
  228. uid_t uid;
  229. double cur_loadavg = 0.0;    /* current load average */
  230. int users = 0;
  231. int procrun, procstop;
  232.  
  233. void initterm();
  234. void getwinsize();
  235. int isloggedin();
  236. void prtinfo();
  237. void stringinit();
  238. void stringspace();
  239. void stringcat();
  240. void stringdump();
  241. void stringprt(const char *format, ...);
  242. int readline();
  243. void whocheck();
  244. int mailseen();
  245. void timeprint();
  246. void ttyprint();
  247. void touch();
  248. char *getenv();
  249. char *ttyname();
  250. char *strcpy1();
  251. char *sysrup();
  252. char *calloc();
  253. char *malloc();
  254. int outc();
  255. int erroutc();
  256. int readutmp();
  257. void readproctab( uid_t uid );
  258. void setsignal( int sig, void (*func)(int) );
  259. extern int getloadavg();
  260. int get_attrs_colors();
  261.  
  262. void main(int argc,register char **argv)
  263. {
  264.     void clearbotl();
  265.     register char *cp;
  266.     char *home;
  267.     extern char *index();
  268.     char *tty;
  269.  
  270.     progname = argv[0];
  271.  
  272. #ifdef HOSTNAME
  273.     gethostname(hostname, sizeof hostname - 1);
  274.     if ((cp = index(hostname, '.')) != NULL)
  275.         *cp = '\0';
  276. #endif
  277.  
  278.     for (argv++; *argv != 0; argv++)
  279.         switch (**argv) {
  280.         case '-':
  281.             for (cp = *argv + 1; *cp; cp++) {
  282.                 switch(*cp) {
  283.                 case 'r' :    /* turn off reverse video */
  284.                     reverse = 0;
  285.                     break;
  286.                 case 'c':
  287.                     clr_bet_ref = 1;
  288.                     break;
  289.                 case 'h':
  290.                     hostprint = 1;
  291.                     break;
  292.                 case 'D':
  293.                     dateprint = 1;
  294.                     break;
  295. #ifdef RWHO
  296.                 case 'H':
  297.                     if (argv[1] == 0 || *argv[1]=='-')
  298.                     {
  299.                         fprintf(stderr,    "%s: -H flag requires argument\n", progname);
  300.                         exit( 1 );
  301.                     }
  302.                     argv++;
  303.                     /* we exclude ourselves */
  304.                     if (strcmp(hostname, *argv) &&
  305.                         strcmp(&hostname[sizeof NETPREFIX - 1], *argv))
  306.                         remotehost[nremotes++].rh_host = *argv;
  307.                     break;
  308. #endif RWHO
  309.                 case 'm':
  310.                     mailcheck = 0;
  311.                     break;
  312.                 case 'p':
  313.                     proccheck = 0;
  314.                     break;
  315.                 case 'l':
  316.                     logcheck = 0;
  317.                     break;
  318.                 case 'b':
  319.                     do_beep = 1;
  320.                     break;
  321.                 case 'i':
  322.                     printid = 1;
  323.                     break;
  324.                 case 'w':
  325.                     window = 1;
  326.                     break;
  327.                 case 'e':
  328.                     emacs = 1;
  329.                     break;
  330.                 case 'd':
  331.                     dbug = 1;
  332.                     break;
  333.                 case 'q':
  334.                     quiet = 1;
  335.                     break;
  336.                 case 's':
  337.                     shortline = 1;
  338.                     break;
  339.                 case 'j':
  340.                     leftline = 1;
  341.                     break;
  342.                 default:
  343.                     fprintf(stderr, "%s: bad flag: %c\n", progname, *cp);
  344.                     exit( 1 );
  345.                 }
  346.             }
  347.             break;
  348.         case '+':
  349.             delay = atoi(*argv + 1);
  350.             if (delay < 10)
  351.                 delay = 10;
  352.             else if (delay > 500)
  353.                 delay = 500;
  354.             synch = 0;    /* no more sync */
  355.             break;
  356.         default:
  357.             fprintf(stderr, "%s: illegal argument `%s'\n", progname, argv[0]);
  358.             exit( 1 );
  359.         }
  360.  
  361.     if ((cp=getenv( "SYSLINE_COLORS" )) || (cp=getenv( "SYSLINE_COLOURS" )))
  362.     {
  363.         get_attrs_colors( cp, "nm=", nm_colors );    /* normal  */
  364.         get_attrs_colors( cp, "rv=", rv_colors );    /* reverse */
  365.     }
  366.  
  367.     if (emacs) {
  368.         reverse = 0;
  369.         cols = 79;
  370.     } else        /* if not to emacs window, initialize terminal dependent info */
  371.         initterm();
  372.  
  373. #ifdef SIGWINCH
  374.     /*
  375.      * When the window size changes and we are the foreground
  376.      * process (true if -w), we get this signal.
  377.      */
  378.     setsignal(SIGWINCH, sigwinch);
  379. #endif
  380.     getwinsize();        /* get window size from ioctl */
  381.  
  382.     /* immediately fork and let the parent die if not emacs mode */
  383.     if (!emacs && !window && !dbug) {
  384.         if (fork())
  385.             exit(0);
  386.         /* pgrp should take care of things, but ignore them anyway */
  387.         setsignal(SIGINT, SIG_IGN);
  388.         setsignal(SIGQUIT, SIG_IGN);
  389.         setsignal(SIGTTOU, SIG_IGN);
  390.     }
  391.     /*
  392.      * When we logoff, init will do a "vhangup()" on this
  393.      * tty which turns off I/O access and sends a SIGHUP
  394.      * signal.  We catch this and thereby clear the status
  395.      * display.  Note that a bug in 4.1bsd caused the SIGHUP
  396.      * signal to be sent to the wrong process, so you had to
  397.      * `kill -HUP' yourself in your .logout file.
  398.      * Do the same thing for SIGTERM, which is the default kill
  399.      * signal.
  400.      */
  401.     setsignal(SIGHUP,  clearbotl);
  402.     setsignal(SIGTERM, clearbotl);
  403.     /*
  404.      * This is so kill -ALRM to force update won't screw us up..
  405.      */
  406.     setsignal(SIGALRM, SIG_IGN);
  407.  
  408.     uid = getuid();
  409.     ourtty = ttyname(2);    /* remember what tty we are on */
  410.     if (printid) {
  411.         printf("%d\n", getpid());
  412.         fflush(stdout);
  413.     }
  414.     dup2(2, 1);
  415.  
  416.     if ((home = getenv("HOME")) == 0)
  417.         home = "";
  418. #ifdef TTY_SUFFIX
  419.     #define DOT "."
  420.     tty = strrchr( ourtty, '/' );
  421.     if (tty)
  422.         tty++;
  423. #else
  424.     #define DOT ""
  425.     tty = "";
  426. #endif
  427.     strcpy1(strcpy1(strcpy1(whofilename,  home), "/.who"DOT), tty );
  428.     strcpy1(strcpy1(strcpy1(whofilename2, home), "/.sysline"DOT), tty );
  429.     strcpy1(strcpy1(strcpy1(lockfilename, home), "/.syslinelock"DOT ), tty );
  430.  
  431.     if (mailcheck)
  432.         if ((username = getenv("USER")) == 0)
  433.             mailcheck = 0;
  434.         else {
  435.             chdir(_PATH_MAILDIR);
  436.             if (stat(username, &mstbuf) >= 0)
  437.                 mailsize = mstbuf.st_size;
  438.             else
  439.                 mailsize = 0;
  440.         }
  441.  
  442.     while (emacs || window || isloggedin())
  443.         if (access(lockfilename, 0) >= 0)
  444.             sleep(60);
  445.         else {
  446.             prtinfo();
  447.             sleep(delay);
  448.             if (clr_bet_ref) {
  449.                 tputs(dsl, 1, outc);
  450.                 fflush(stdout);
  451.                 sleep(5);
  452.             }
  453.             revtime = (1 + revtime) % REVOFF;
  454.         }
  455.     clearbotl();
  456.     /*NOTREACHED*/
  457. }
  458.  
  459. int isloggedin()
  460. {
  461.     /*
  462.      * you can tell if a person has logged out if the owner of
  463.      * the tty has changed
  464.      */
  465.     struct stat statbuf;
  466.  
  467.     return fstat(2, &statbuf) == 0 && statbuf.st_uid == uid;
  468. }
  469.  
  470. int readutmp(nflag)
  471.     char nflag;
  472. {
  473.     static time_t lastmod;        /* initially zero */
  474.     static off_t utmpsize;        /* ditto */
  475.     struct stat st;
  476.  
  477.     if (ut < 0 && (ut = open(_PATH_UTMP, 0)) < 0) {
  478.         fprintf(stderr, "%s: can't open %s\n", progname, _PATH_UTMP);
  479.         exit(1);
  480.     }
  481.     if (fstat(ut, &st) < 0 || st.st_mtime == lastmod)
  482.         return 0;
  483.     lastmod = st.st_mtime;
  484.     if (utmpsize != st.st_size)
  485.     {
  486.         off_t old_utmpsize;
  487.         
  488.         old_utmpsize = utmpsize;
  489.         utmpsize = st.st_size;
  490.         nentries = utmpsize / sizeof (struct utmp);
  491.         if (old == 0) {
  492.             old = (struct utmp *)calloc(utmpsize, 1);
  493.             new = (struct utmp *)calloc(utmpsize, 1);
  494.         } else {
  495.             old = (struct utmp *)realloc((char *)old, utmpsize);
  496.             if (utmpsize>old_utmpsize)
  497.                 /* clear the extension */
  498.                 memset( ((char *)old)+old_utmpsize, 0, (size_t)(utmpsize-old_utmpsize) );
  499.             new = (struct utmp *)realloc((char *)new, utmpsize);
  500.             free(ttystatus);
  501.         }
  502.         ttystatus = malloc(nentries * sizeof *ttystatus);
  503.         if (old == 0 || new == 0 || ttystatus == 0) {
  504.             fprintf(stderr, "%s: out of memory\n", progname);
  505.             exit(1);
  506.         }
  507.     }
  508.     lseek(ut, 0L, 0);
  509.     (void) read(ut, (char *) (nflag ? new : old), utmpsize);
  510.     return 1;
  511. }
  512.  
  513. void prtinfo()
  514. {
  515.     int on, off;
  516.     register i;
  517.     char fullprocess;
  518.  
  519.     stringinit();
  520. #ifdef SIGWINCH
  521.     if (winchanged) {
  522.         winchanged = 0;
  523.         getwinsize();
  524.         mustclear = 1;
  525.     }
  526. #endif
  527. #ifdef WHO
  528.     /* 
  529.      * check for file named .who in the home directory, or, 
  530.      * if it does not exist, .sysline 
  531.      */
  532.     whocheck();
  533. #endif
  534.     timeprint();
  535.     /*
  536.      * if mail is seen, don't print rest of info, just the mail
  537.      * reverse new and old so that next time we run, we won't lose log
  538.      * in and out information
  539.      */
  540.     if (mailcheck && (sawmail = mailseen()))
  541.         goto bottom;
  542. #ifdef HOSTNAME
  543. #ifdef RWHO
  544.     for (i = 0; i < nremotes; i++) {
  545.         char *tmp;
  546.  
  547.         stringspace();
  548.         tmp = sysrup(remotehost + i);
  549.         stringcat(tmp, strlen(tmp));
  550.     }
  551. #endif
  552.     /*
  553.      * print hostname info if requested
  554.      */
  555.     if (hostprint) {
  556.         stringspace();
  557.         stringcat(hostname, -1);
  558.     }
  559. #endif
  560.     /*
  561.      * print load average and difference between current load average
  562.      * and the load average 5 minutes ago
  563.      */
  564.      
  565.     if (getloadavg(_avenrun, 3) > 0) {
  566.         double diff;
  567.  
  568.         stringspace();
  569.  
  570.         if ((diff = _avenrun[0] - _avenrun[1]) < 0.0)
  571.             stringprt("%.1f %.1f", _avenrun[0],  diff);
  572.         else
  573.             stringprt("%.1f +%.1f", _avenrun[0], diff);
  574.         cur_loadavg = _avenrun[0];
  575.     }
  576.     
  577.     /*
  578.      * print log on and off information
  579.      */
  580.     stringspace();
  581.     fullprocess = 1;
  582. #ifdef MAXLOAD
  583.     if (cur_loadavg > MAXLOAD)
  584.         fullprocess = 0;    /* too loaded to run */
  585. #endif
  586.     /*
  587.      * Read utmp file (logged in data) only if we are doing a full
  588.      * process, or if this is the first time and we are calculating
  589.      * the number of users.
  590.      */
  591.     on = off = 0;
  592.     if (users == 0) {        /* first time */
  593.         if (readutmp(0))
  594.             for (i = 0; i < nentries; i++)
  595.                 if (old[i].ut_name[0])
  596.                     if (old[i].ut_type==USER_PROCESS)
  597.                         users++;
  598.     } else if (fullprocess && readutmp(1)) {
  599.         struct utmp *tmp;
  600.  
  601.         users = 0;
  602.  
  603.         for (i = 0; i < nentries; i++) 
  604.         {
  605.             if (old[i].ut_name[0] == '\0')
  606.             {
  607.                 if (new[i].ut_type==USER_PROCESS)
  608.                 {
  609.                     ttystatus[i] = ON;
  610.                     on++;
  611.                 }
  612.                 else
  613.                     ttystatus[i] = NOCH;
  614.             } 
  615.             else
  616.             if (new[i].ut_name[0] == '\0')
  617.             {
  618.                 if (old[i].ut_type==USER_PROCESS)
  619.                 {
  620.                     ttystatus[i] = OFF;
  621.                     off++;
  622.                 }
  623.                 else
  624.                     ttystatus[i] = NOCH;
  625.             }
  626.             else
  627.             if (strncmp(old[i].ut_name, new[i].ut_name, NAME_SIZE) == 0)
  628.             {
  629.                 if (old[i].ut_type==new[i].ut_type)
  630.                     ttystatus[i] = NOCH;
  631.                 else
  632.                 {
  633.                     if (old[i].ut_type==USER_PROCESS)
  634.                     {
  635.                         ttystatus[i] = OFF;
  636.                         off++;
  637.                     }
  638.                     else
  639.                     if (new[i].ut_type==USER_PROCESS)
  640.                     {
  641.                         ttystatus[i] = ON;
  642.                         on++;
  643.                     }
  644.                     else
  645.                         ttystatus[i] = NOCH;
  646.                 }
  647.             }
  648.             else
  649.             {
  650.                 ttystatus[i] = ON | OFF;
  651.                 on++;
  652.                 off++;
  653.             }
  654.             if (new[i].ut_name[0])
  655.                 if (new[i].ut_type==USER_PROCESS)
  656.                     users++;
  657.         }
  658.         tmp = new;
  659.         new = old;
  660.         old = tmp;
  661.     }
  662.     /*
  663.      * Print:
  664.      *     1.  number of users
  665.      *    2.  a * for unread mail
  666.      *    3.  a - if load is too high
  667.      *    4.  number of processes running and stopped
  668.      */
  669.     stringprt("%du", users);
  670.     if (mailsize > 0 && mstbuf.st_mtime >= mstbuf.st_atime)
  671.         stringcat("*", -1);
  672.     if (!fullprocess && (proccheck || logcheck))
  673.         stringcat("-", -1);
  674.  
  675.     if (fullprocess && proccheck) {
  676.         readproctab( uid );
  677.         if (procrun > 0 || procstop > 0) {
  678.             stringspace();
  679.             if (procrun > 0 && procstop > 0)
  680.                 stringprt("%dr %ds", procrun, procstop);
  681.             else if (procrun > 0)
  682.                 stringprt("%dr", procrun);
  683.             else
  684.                 stringprt("%ds", procstop);
  685.         }
  686.     }
  687.     /*
  688.      * If anyone has logged on or off, and we are interested in it,
  689.      * print it out.
  690.      */
  691.     if (logcheck) {
  692.         /* old and new have already been swapped */
  693.         if (on) {
  694.             stringspace();
  695.             stringcat("on:", -1);
  696.             for (i = 0; i < nentries; i++)
  697.                 if (ttystatus[i] & ON) {
  698.                     stringprt(" %.8s", old[i].ut_name);
  699.                     ttyprint(old[i].ut_line);
  700.                 }
  701.         }
  702.         if (off) {
  703.             stringspace();
  704.             stringcat("off:", -1);
  705.             for (i = 0; i < nentries; i++)
  706.                 if (ttystatus[i] & OFF) {
  707.                     stringprt(" %.8s", new[i].ut_name);
  708.                     ttyprint(new[i].ut_line);
  709.                 }
  710.         }
  711.     }
  712. bottom:
  713.         /* dump out what we know */
  714.     stringdump();
  715. }
  716.  
  717. void timeprint()
  718. {
  719.     long curtime;
  720.     struct tm *tp, *localtime();
  721.     static int beepable = 1;
  722.  
  723.         /* always print time */
  724.     time(&curtime);
  725.     tp = localtime(&curtime);
  726.     if (dateprint)
  727.         stringprt("%.11s", ctime(&curtime));
  728. #if defined( CLOCK24 )
  729.     stringprt("%02d:%02d", tp->tm_hour, tp->tm_min);
  730. #else    
  731.     stringprt("%02d:%02d", tp->tm_hour > 12 ? tp->tm_hour - 12 :
  732.         (tp->tm_hour == 0 ? 12 : tp->tm_hour), tp->tm_min);
  733. #endif
  734.     if (synch)            /* sync with clock */
  735.         delay = 60 - tp->tm_sec;
  736.     /*
  737.      * Beepable is used to insure that we get at most one set of beeps
  738.      * every half hour.
  739.      */
  740.     if (do_beep)
  741.         if (beepable) {
  742.             if (tp->tm_min == 30) {
  743.                 tputs(bel, 1, outc);
  744.                 fflush(stdout);
  745.                 beepable = 0;
  746.             } else if (tp->tm_min == 0) {
  747.                 tputs(bel, 1, outc);
  748.                 fflush(stdout);
  749.                 sleep(2);
  750.                 tputs(bel, 1, outc);
  751.                 fflush(stdout);
  752.                 beepable = 0;
  753.             }
  754.         } else
  755.             if (tp->tm_min != 0 && tp->tm_min != 30)
  756.                 beepable = 1;
  757. }
  758.  
  759. /*
  760.  * whocheck -- check for file named .who or .sysline and print it on the who line first
  761.  */
  762. void whocheck()
  763. {
  764.     int chss;
  765.     register char *p;
  766.     char buff[81];
  767.     int whofile;
  768.  
  769.     if ((whofile = open(whofilename, 0)) < 0 &&
  770.         (whofile = open(whofilename2, 0)) < 0)
  771.         return;
  772.     chss = read(whofile, buff, sizeof buff - 1);
  773.     close(whofile);
  774.     if (chss <= 0)
  775.         return;
  776.     buff[chss] = '\0';
  777.     /*
  778.      * Remove all line feeds, and replace by spaces if they are within
  779.      * the message, else replace them by nulls.
  780.      */
  781.     for (p = buff; *p;)
  782.         if (*p == '\n')
  783.             if (p[1])
  784.                 *p++ = ' ';
  785.             else
  786.                 *p = '\0';
  787.         else
  788.             p++;
  789.     stringcat(buff, p - buff);
  790.     stringspace();
  791. }
  792.  
  793. /*
  794.  * ttyprint -- given the name of a tty, print in the string buffer its
  795.  * short name surrounded by parenthesis.
  796.  * ttyxx is printed as (xx)
  797.  * console is printed as (cty)
  798.  */
  799. void ttyprint(name)
  800.     char *name;
  801. {
  802.     if (strncmp(name, "tty", 3) == 0)
  803.         stringprt("(%.*s)", LINE_SIZE - 3, name + 3);
  804.     else if (strcmp(name, "console") == 0)
  805.         stringcat("(cty)", -1);
  806.     else
  807.         stringprt("(%.*s)", LINE_SIZE, name);
  808. }
  809.  
  810. /*
  811.  * mail checking function
  812.  * returns 0 if no mail seen
  813.  */
  814. int mailseen()
  815. {
  816.     FILE *mfd;
  817.     register n;
  818.     register char *cp;
  819.     char lbuf[100], sendbuf[100], *bufend;
  820.     char seenspace;
  821.     int retval = 0;
  822.  
  823.     if (stat(username, &mstbuf) < 0) {
  824.         mailsize = 0;
  825.         return 0;
  826.     }
  827.     if (mstbuf.st_size <= mailsize || (mfd = fopen(username,"r")) == NULL) {
  828.         mailsize = mstbuf.st_size;
  829.         return 0;
  830.     }
  831.     fseek(mfd, mailsize, 0);
  832.     while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0 &&
  833.            strncmp(lbuf, "From ", 5) != 0)
  834.         ;
  835.     if (n < 0) {
  836.         stringspace();
  837.         stringcat("Mail has just arrived", -1);
  838.         goto out;
  839.     }
  840.     retval = 1;
  841.     /*
  842.      * Found a From line, get second word, which is the sender,
  843.      * and print it.
  844.      */
  845.     for (cp = lbuf + 5; *cp && *cp != ' '; cp++)    /* skip to blank */
  846.         ;
  847.     *cp = '\0';                    /* terminate name */
  848.     stringspace();
  849.     stringprt("Mail from %s ", lbuf + 5);
  850.     /*
  851.      * Print subject, and skip over header.
  852.      */
  853.     while ((n = readline(mfd, lbuf, sizeof lbuf)) > 0)
  854.         if (strncmp(lbuf, "Subject:", 8) == 0)
  855.             stringprt("on %s ", lbuf + 9);
  856.     if (!emacs)
  857.         stringcat(arrows, 2);
  858.     else
  859.         stringcat(": ", 2);
  860.     if (n < 0)                    /* already at eof */
  861.         goto out;
  862.     /*
  863.      * Print as much of the letter as we can.
  864.      */
  865.     cp = sendbuf;
  866.     if ((n = cols - chars) > sizeof sendbuf - 1)
  867.         n = sizeof sendbuf - 1;
  868.     bufend = cp + n;
  869.     seenspace = 0;
  870.     while ((n = readline(mfd, lbuf, sizeof lbuf)) >= 0) {
  871.         register char *rp;
  872.  
  873.         if (strncmp(lbuf, "From ", 5) == 0)
  874.             break;
  875.         if (cp >= bufend)
  876.             continue;
  877.         if (!seenspace) {
  878.             *cp++ = ' ';        /* space before lines */
  879.             seenspace = 1;
  880.         }
  881.         rp = lbuf;
  882.         while (*rp && cp < bufend)
  883.             if (isspace(*rp)) {
  884.                 if (!seenspace) {
  885.                     *cp++ = ' ';
  886.                     seenspace = 1;
  887.                 }
  888.                 rp++;
  889.             } else {
  890.                 *cp++ = *rp++;
  891.                 seenspace = 0;
  892.             }
  893.     }
  894.     *cp = 0;
  895.     stringcat(sendbuf, -1);
  896.     /*
  897.      * Want to update write time so a star will
  898.      * appear after the number of users until the
  899.      * user reads his mail.
  900.      */
  901. out:
  902.     mailsize = linebeg;
  903.     fclose(mfd);
  904.     touch(username);
  905.     return retval;
  906. }
  907.  
  908. /*
  909.  * readline -- read a line from fp and store it in buf.
  910.  * return the number of characters read.
  911.  */
  912. int readline(fp, buf, n)
  913.     register FILE *fp;
  914.     char *buf;
  915.     register n;
  916. {
  917.     register c=0;
  918.     register char *cp = buf;
  919.  
  920.     linebeg = ftell(fp);        /* remember loc where line begins */
  921.     cp = buf;
  922.     while (--n > 0 && (c = getc(fp)) != EOF && c != '\n')
  923.         *cp++ = c;
  924.     *cp = 0;
  925.     if (c == EOF && cp - buf == 0)
  926.         return -1;
  927.     return cp - buf;
  928. }
  929.  
  930.  
  931. /*
  932.  * string hacking functions
  933.  */
  934.  
  935. void stringinit()
  936. {
  937.     sp = strarr;
  938.     chars = 0;
  939. }
  940.  
  941. void stringprt(const char *format, ...)
  942. {
  943.     char tempbuf[150];
  944.     va_list ap;
  945.  
  946.     va_start( ap, format );
  947.     vsprintf(tempbuf, format, ap);
  948.     va_end( ap );
  949.     stringcat( tempbuf, -1 );
  950. }
  951.  
  952. void stringdump()
  953. {
  954.     char bigbuf[sizeof strarr + 200];
  955.     register char *bp = bigbuf;
  956.     register int i;
  957.  
  958.     if (!emacs) {
  959.         if (sawmail)
  960.             bp = strcpy1(bp, bel);
  961. #ifdef TERMINFO
  962.         if (!window && status_line_esc_ok)
  963. #else
  964.         if (!window && eslok)
  965. #endif
  966.             bp = strcpy1(bp, tparm(tsl,
  967.                 leftline ? 0 : cols - chars));
  968.         else {
  969.             bp = strcpy1(bp, tsl);
  970.             if (!shortline && !leftline)
  971.                 for (i = cols - chars; --i >= 0;)
  972.                     *bp++ = ' ';
  973.         }
  974.  
  975.         if (reverse && revtime != 0)
  976.         {
  977.             if (*rv_colors)
  978.                 bp = strcpy1( bp, rv_colors );
  979.             else
  980.                 bp = strcpy1( bp, rev_out);
  981.         }
  982.         else
  983.             bp = strcpy1( bp, nm_colors );
  984.     }
  985.     *sp = 0;
  986.     bp = strcpy1(bp, strarr);
  987.     if (!emacs) {
  988.         if (reverse)
  989.             bp = strcpy1(bp, rev_end);
  990.         bp = strcpy1(bp, fsl);
  991.         if (sawmail)
  992.             bp = strcpy1(strcpy1(bp, bel), bel);
  993.         *bp = 0;
  994.         tputs(bigbuf, 1, outc);
  995.         if (mustclear) {
  996.             mustclear = 0;
  997.             tputs(clr_eol, 1, outc);
  998.         }
  999.         if (dbug)
  1000.             putchar('\n');
  1001.         fflush(stdout);
  1002.     } else
  1003.         write(2, bigbuf, bp - bigbuf);
  1004. }
  1005.  
  1006. void stringspace()
  1007. {
  1008.     if (reverse && revtime != 0) {
  1009. #ifdef TERMINFO
  1010.         stringcat(rev_end,
  1011.             magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
  1012.         stringcat(" ", 1);
  1013.         if (*rv_colors)
  1014.             stringcat( rv_colors, 0 );
  1015.         else
  1016.             stringcat(rev_out,
  1017.             magic_cookie_glitch <= 0 ? 0 : magic_cookie_glitch);
  1018. #else
  1019.         stringcat(rev_end, 0);
  1020.         stringcat(" ", 1);
  1021.         if (*rv_colors)
  1022.             stringcat( rv_colors, 0 );
  1023.         else
  1024.             stringcat(rev_out, 0);
  1025. #endif TERMINFO
  1026.     } else
  1027.         stringcat(" ", 1);
  1028. }
  1029.  
  1030. /*
  1031.  * stringcat :: concatenate the characters in string str to the list we are
  1032.  *             building to send out.
  1033.  * str - the string to print. may contain funny (terminal control) chars.
  1034.  * n  - the number of printable characters in the string
  1035.  *    or if -1 then str is all printable so we can truncate it,
  1036.  *    otherwise don't print only half a string.
  1037.  */
  1038. void stringcat(str, n)
  1039.     register char *str;
  1040.     register n;
  1041. {
  1042.     register char *p = sp;
  1043.  
  1044.     if (n < 0) {                /* truncate */
  1045.         n = cols - chars;
  1046.         while ((*p++ = *str++) && --n >= 0)
  1047.             ;
  1048.         p--;
  1049.         chars += p - sp;
  1050.         sp = p;
  1051.     } else if (chars + n <= cols) {    /* don't truncate */
  1052.         while ((*p++ = *str++))
  1053.             ;
  1054.         chars += n;
  1055.         sp = p - 1;
  1056.     }
  1057. }
  1058.  
  1059. /*
  1060.  * touch :: update the modify time of a file.
  1061.  */
  1062. void touch(name)
  1063.     char *name;        /* name of file */
  1064. {
  1065.     register fd;
  1066.     char buf;
  1067.  
  1068.     if ((fd = open(name, 2)) >= 0) {
  1069.         read(fd, &buf, 1);        /* get first byte */
  1070.         lseek(fd, 0L, 0);        /* go to beginning */
  1071.         write(fd, &buf, 1);        /* and rewrite first byte */
  1072.         close(fd);
  1073.     }
  1074. }
  1075.  
  1076.  
  1077. /*
  1078.  * clearbotl :: clear bottom line.
  1079.  * called when process quits or is killed.
  1080.  * it clears the bottom line of the terminal.
  1081.  */
  1082. void clearbotl()
  1083. {
  1084.     register int fd;
  1085.  
  1086.     setsignal(SIGALRM, exit);
  1087.     alarm(30);    /* if can't open in 30 secs, just die */
  1088.     if (!emacs && (fd = open(ourtty, 1)) >= 0) {
  1089.         write(fd, dsl, strlen(dsl));
  1090.         close(fd);
  1091.     }
  1092. #ifdef PROF
  1093.     if (chdir(_PATH_SYSLINE) < 0)
  1094.         (void) chdir(_PATH_TMP);
  1095. #endif
  1096.     exit(0);
  1097. }
  1098.  
  1099. #ifdef TERMINFO
  1100. void initterm()
  1101. {
  1102.     char *term;
  1103.     static char standbuf[40];
  1104.  
  1105.     term = getenv("TERM");
  1106.     setupterm(0, 1, 0);
  1107.     if (!window && !has_status_line) {
  1108.         /* not an appropriate terminal */
  1109.         if (!quiet)
  1110.            fprintf(stderr, "%s: no status-line capability for terminal type `%s'\n", progname, term);
  1111.         exit(1);
  1112.     }
  1113.  
  1114.     if (!window && init_2string != NULL) 
  1115.     {
  1116.         tputs(init_2string, 1, erroutc);
  1117.         fflush(stdout);
  1118.     }
  1119.  
  1120.     if (window || status_line_esc_ok) {
  1121.         if (set_attributes) {
  1122.             /* reverse video mode */
  1123.             strcpy(standbuf,
  1124.                 tparm(set_attributes,0,0,1,0,0,0,0,0,0));
  1125.             rev_out = standbuf;
  1126.             rev_end = exit_attribute_mode;
  1127.         } else if (enter_standout_mode && exit_standout_mode) {
  1128.             rev_out = enter_standout_mode;
  1129.             rev_end = exit_standout_mode;
  1130.         } else
  1131.             rev_out = rev_end = "";
  1132.     } else
  1133.         rev_out = rev_end = "";
  1134.     cols = width_status_line;
  1135.     hasws = cols>=0;
  1136.     if (!hasws)
  1137.         cols = columns;        
  1138.     cols--;        /* avoid cursor wraparound */
  1139.     if (!strncmp(term, "h19", 3))
  1140.         arrows = "\033Fhh\033G";    /* "two tiny graphic arrows" */
  1141.     else
  1142.         arrows = ARROWS;
  1143.     if (bell!=NULL)
  1144.         bel = bell;
  1145.     else
  1146.         bel = "\007";
  1147.     if (window) 
  1148.     {
  1149.         strcpy( tsl,   "\r" );
  1150.         strcpy( dsl,   "\r" );
  1151.         strcpy( dsl+1, clr_eol );
  1152.         if (leftline)
  1153.             strcpy( fsl, clr_eol );
  1154.         else
  1155.             strcpy( fsl, "" );
  1156.     }
  1157.     else
  1158.     {
  1159.         strcpy( tsl, to_status_line   );
  1160.         strcpy( fsl, from_status_line );
  1161.         strcpy( dsl, dis_status_line  );
  1162.     }
  1163. }
  1164.  
  1165. #else    /* TERMCAP */
  1166. void initterm()
  1167. {
  1168. #if defined( _HAVE_GNU_TERMCAP_ )
  1169.     int res;
  1170. #else
  1171.     static char tbuf[2048];
  1172. #endif
  1173.     char *term, *cp;
  1174.     char is2[40];
  1175.     extern char *UP;
  1176.  
  1177.     if ((term = getenv("TERM")) == NULL) {
  1178.         if (!quiet)
  1179.             fprintf(stderr, "%s: no TERM variable in environment\n", progname);
  1180.         exit(1);
  1181.     }
  1182.     
  1183. #if defined( _HAVE_GNU_TERMCAP_ )
  1184.     if ((res=tgetent(NULL, term))<=0)
  1185.     {
  1186.         if (!quiet)
  1187.         {
  1188.             if (res==-1)
  1189.                 fprintf(stderr,    "%s: could not access termcap database\n", progname);
  1190.             else
  1191.                 fprintf(stderr,    "%s: unknown terminal `%s'\n", progname, term);
  1192.         }
  1193.         exit(1);
  1194.     }
  1195. #else
  1196.     if (tgetent(tbuf, term) <= 0) {
  1197.         if (!quiet)
  1198.             fprintf(stderr, "%s: unknown terminal type `%s'\n", progname, term);
  1199.         exit(1);
  1200.     }
  1201. #endif    
  1202.     if (!window && tgetflag("hs") <= 0) {
  1203.         if (!strncmp(term, "h19", 3)) {
  1204.             /* for upward compatability with h19sys */
  1205.             strcpy(tsl,
  1206.                 "\033j\033x5\033x1\033Y8%+ \033o");
  1207.             strcpy(fsl, "\033k\033y5");
  1208.             strcpy(dsl, "\033y1");
  1209.             strcpy(rev_out, "\033p");
  1210.             strcpy(rev_end, "\033q");
  1211.             arrows = "\033Fhh\033G";
  1212.             cols = 80;
  1213.             UP = "\b";
  1214.             return;
  1215.         }
  1216.         if (!quiet)
  1217.             fprintf(stderr,    "%s: no status-line capability for terminal `%s'\n", progname, term);
  1218.         exit(1);
  1219.     }
  1220.     cp = is2;
  1221.     if (!window && tgetstr("i2", &cp)) {
  1222.         /* someday tset will do this */
  1223.         tputs(is2, 1, erroutc);
  1224.         fflush(stdout);
  1225.     }
  1226.  
  1227.     /* the "-1" below is to avoid cursor wraparound problems */
  1228.     cols = tgetnum("ws");
  1229.     hasws = cols >= 0;
  1230.     if (!hasws)
  1231.         cols = tgetnum("co");
  1232.     cols -= 1;
  1233. #if defined( _HAVE_GNU_TERMCAP_ )
  1234.     if (!(bel=tgetstr("bl", NULL)))
  1235.         bel = "\007";
  1236. #else
  1237.     cp = bel;
  1238.     if (!tgetstr("bl", &cp))
  1239.         strcpy( bel, "\007" );
  1240. #endif
  1241.  
  1242.     if (window) {
  1243.         strcpy(tsl, "\r");
  1244.         cp = dsl;        /* use the clear line sequence */
  1245.         *cp++ = '\r';
  1246.         tgetstr("ce", &cp);
  1247.         if (leftline)
  1248.             strcpy(fsl, dsl + 1);
  1249.         else
  1250.             strcpy(fsl, "");
  1251.     } else {
  1252.         cp = tsl;
  1253.         tgetstr("ts", &cp);
  1254.         cp = fsl;
  1255.         tgetstr("fs", &cp);
  1256.         cp = dsl;
  1257.         tgetstr("ds", &cp);
  1258.         eslok = tgetflag("es");
  1259.     }
  1260.     if (eslok || window) {
  1261. #if defined( _HAVE_GNU_TERMCAP_ )
  1262.         rev_out = tgetstr("so", NULL);
  1263.         rev_end = tgetstr("se", NULL);
  1264.         clr_eol = tgetstr("ce", NULL);
  1265. #else
  1266.         cp = rev_out;
  1267.         tgetstr("so", &cp);
  1268.         cp = rev_end;
  1269.         tgetstr("se", &cp);
  1270.         cp = clr_eol;
  1271.         tgetstr("ce", &cp);
  1272. #endif
  1273.     } else
  1274.         reverse = 0;            /* turn off reverse video */
  1275.     UP = "\b";
  1276.     if (!strncmp(term, "h19", 3))
  1277.         arrows = "\033Fhh\033G";    /* "two tiny graphic arrows" */
  1278.     else
  1279.         arrows = ARROWS;
  1280. }
  1281. #endif TERMINFO
  1282.  
  1283. #ifdef RWHO
  1284. char *
  1285. sysrup(hp)
  1286.     register struct remotehost *hp;
  1287. {
  1288.     char filename[100];
  1289.     struct whod wd;
  1290. #define WHOD_HDR_SIZE (sizeof (wd) - sizeof (wd.wd_we))
  1291.     static char buffer[50];
  1292.     time_t now;
  1293.  
  1294.     /*
  1295.      * rh_file is initially 0.
  1296.      * This is ok since standard input is assumed to exist.
  1297.      */
  1298.     if (hp->rh_file == 0) {
  1299.         /*
  1300.          * Try rwho hostname file, and if that fails try ucbhostname.
  1301.          */
  1302.         (void) strcpy1(strcpy1(filename, _PATH_RWHO), hp->rh_host);
  1303.         if ((hp->rh_file = open(filename, 0)) < 0) {
  1304.             (void) strcpy1(strcpy1(strcpy1(filename, _PATH_RWHO),
  1305.                 NETPREFIX), hp->rh_host);
  1306.             hp->rh_file = open(filename, 0);
  1307.         }
  1308.     }
  1309.     if (hp->rh_file < 0) {
  1310.         (void) sprintf(buffer, "%s?", hp->rh_host);
  1311.         return(buffer);
  1312.     }
  1313.     (void) lseek(hp->rh_file, (off_t)0, 0);
  1314.     if (read(hp->rh_file, (char *)&wd, WHOD_HDR_SIZE) != WHOD_HDR_SIZE) {
  1315.         (void) sprintf(buffer, "%s ?", hp->rh_host);
  1316.         return(buffer);
  1317.     }
  1318.     (void) time(&now);
  1319.     if (now - wd.wd_recvtime > DOWN_THRESHOLD) {
  1320.         long interval;
  1321.         long days, hours, minutes;
  1322.  
  1323.         interval = now - wd.wd_recvtime;
  1324.         minutes = (interval + 59) / 60;    /* round to minutes */
  1325.         hours = minutes / 60;        /* extract hours from minutes */
  1326.         minutes %= 60;            /* remove hours from minutes */
  1327.         days = hours / 24;        /* extract days from hours */
  1328.         hours %= 24;            /* remove days from hours */
  1329.         if (days > 7 || days < 0)
  1330.             (void) sprintf(buffer, "%s down", hp->rh_host);
  1331.         else if (days > 0)
  1332.             (void) sprintf(buffer, "%s %d+%d:%02d",
  1333.                 hp->rh_host, (int)days, (int)hours, (int)minutes);
  1334.         else
  1335.             (void) sprintf(buffer, "%s %d:%02d",
  1336.                 hp->rh_host, (int)hours, (int)minutes);
  1337.     } else
  1338.         (void) sprintf(buffer, "%s %.1f",
  1339.             hp->rh_host, wd.wd_loadav[0]/100.0);
  1340.     return buffer;
  1341. }
  1342. #endif RWHO
  1343.  
  1344. void getwinsize()
  1345. {
  1346. #ifdef TIOCGWINSZ
  1347.     struct winsize winsize;
  1348.  
  1349.     /* the "-1" below is to avoid cursor wraparound problems */
  1350.     if (!hasws
  1351.      && ioctl(2, TIOCGWINSZ, (char *)&winsize) >= 0 && winsize.ws_col != 0)
  1352.         cols = winsize.ws_col - 1;
  1353. #endif
  1354. }
  1355.  
  1356. #ifdef SIGWINCH
  1357. void sigwinch()
  1358. {
  1359.     winchanged++;
  1360. }
  1361. #endif
  1362.  
  1363. char *
  1364. strcpy1(p, q)
  1365.     register char *p, *q;
  1366. {
  1367.     while ((*p++ = *q++))
  1368.         ;
  1369.     return p - 1;
  1370. }
  1371.  
  1372. /* 
  1373.  * inspired upon ncurses's unctrl() -- bjd
  1374.  */
  1375. #undef  unctrl
  1376. #define unctrl(x)    ((x&0x60)!=0&&x!=0x7F)?(x):(x==0x7F?'?':(x|0x40))
  1377.  
  1378. int outc(c)
  1379.     char c;
  1380. {
  1381.     if (dbug)
  1382.         printf("%c", unctrl(c));
  1383.     else
  1384.         putchar(c);
  1385.     return( 1 );
  1386. }
  1387.  
  1388. int erroutc(c)
  1389.     char c;
  1390. {
  1391.     if (dbug)
  1392.         fprintf(stderr, "%c", unctrl(c));
  1393.     else
  1394.         putc(c, stderr);
  1395.     return( 1 );
  1396. }
  1397.  
  1398.  
  1399.  /* 
  1400.   * following readproctab() is derived from Michael K. Johnson's function
  1401.   * take_snapshot in his procps package, stripped of redundancy and
  1402.   * customized to fit our purposes - bjd 
  1403.   */
  1404.  
  1405.  #define PZERO    15
  1406.  
  1407.  void readproctab( uid_t uid )
  1408.  {
  1409.     DIR *proc;
  1410.     static struct direct *ent;
  1411.     static char filename[80];
  1412.     static char stat_str[4096];
  1413.     char process_state;
  1414.     int  process_ppid, process_pgrp, process_priority;
  1415.     struct stat sb;
  1416.     int fd, nr_read;
  1417.  
  1418.     procrun = procstop = 0;
  1419.     proc = opendir("/proc");
  1420.  
  1421.     while ((ent = readdir(proc)))
  1422.     {
  1423.          /*
  1424.           * I'm assuming anything that starts with a digit is a
  1425.           * valid process directory.  Since the kernel has absolute
  1426.           * control over the /proc directory, this is a valid 
  1427.           * assumption, I think.            - bjd
  1428.           */
  1429.         if (!isdigit(*ent->d_name))
  1430.             continue;
  1431.             
  1432.         sprintf(filename, "/proc/%s", ent->d_name);
  1433.         if ((stat(filename, &sb))==-1)
  1434.             continue;
  1435.         if(sb.st_uid != uid) 
  1436.                 continue;
  1437.         strcat(filename, "/stat");
  1438.         if ((fd=open(filename, O_RDONLY, 0))==-1)
  1439.             continue;
  1440.            nr_read = read(fd, stat_str, sizeof(stat_str));
  1441.         stat_str[nr_read]='\0';
  1442.         close(fd);
  1443.     
  1444.         sscanf(stat_str, "%*d %*s %c %d %d %*d %*d %*d %*u %*u \
  1445.                  %*u %*u %*u %*d %*d %*d %*d %*d %d",
  1446.                 &process_state, &process_ppid, 
  1447.                 &process_pgrp,  &process_priority );
  1448.         if (process_pgrp==0 || process_ppid==1)
  1449.                 continue;
  1450.         switch (process_state) 
  1451.         {
  1452.             case 'T':procstop++;
  1453.                  break;
  1454.             case 'S':
  1455.                  /*
  1456.                   * Sleep can mean waiting for a signal or just
  1457.                   * in a disk or page wait queue ready to run.
  1458.                   * We can tell if it is the latter by the pri
  1459.                   * being negative.
  1460.                   */
  1461.                  if (process_priority > 0/*PZERO*/)
  1462.                     procrun++;
  1463.                  break;
  1464.             case 'D':
  1465.             case 'R':procrun++;
  1466.         }
  1467.     } /* end of the while loop */
  1468.     closedir(proc);
  1469.  }
  1470.  
  1471.  void setsignal( int sig, void (*func)(int) )
  1472.  {
  1473.     struct sigaction sa;
  1474.  
  1475.     sa.sa_handler = func;
  1476.     sigemptyset( &sa.sa_mask );
  1477.     sa.sa_flags = 0;
  1478.     if (sig!=SIGALRM) 
  1479.     {
  1480. #ifdef    SA_RESTART
  1481.         sa.sa_flags |= SA_RESTART;
  1482. #endif
  1483.     }
  1484.     sigaction( sig, &sa, NULL );
  1485.  }
  1486.  
  1487.  int get_attrs_colors( char *str, char *token, char *store )
  1488.  {
  1489.     char *s=str;
  1490.     
  1491.     if ((s=strstr( s, token )))
  1492.     {
  1493.         int n, val[8];    /* max. 8 attributes/colors */ 
  1494.  
  1495.         s += 3;    /* strlen of "nm=" and "rv=" */
  1496.         if ((n=sscanf( s, "%d;%d;%d;%d;%d;%d;%d;%d", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7] ))>=1)
  1497.         {
  1498.             register int i;
  1499.             register char *t = store;
  1500.             
  1501.             t += sprintf( t, "\033[" );
  1502.             for ( i=0; i<n; i++ )
  1503.                 t += sprintf( t, "%d;", val[i] );
  1504.             *(t-1) = 'm';
  1505.         
  1506.             return( 1 );
  1507.         }
  1508.     }
  1509.     return( 0 );
  1510.  }
  1511.